package com.baymax.datascope.handler;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baymax.common.Constant;
import com.baymax.datascope.annotation.DataPermission;
import com.baymax.datascope.enums.DataScopeEnum;
import com.baymax.utils.SecurityUtil;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 数据权限解析器，拼接条件sql
 *
 * @Author: baymax
 * @CreateTime: 2023/4/2 15:26
 * @Version: 1.0
 */
public class MybatisPlusDataPermissionHandler {

    private final static Map<String, DataPermission> dataPermissionCacheMap = new ConcurrentHashMap<>();

    public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) {
        // 如果是管理员，则放行
        if (SecurityUtil.isAdmin()) {
            return where;
        }
        // 获取自定义注解
        DataPermission dataPermission = findDataScopeAnnotation(mappedStatementId);
        // 构建查询条件
        String sql = buildDataFilter(dataPermission, isSelect);
        if (StrUtil.EMPTY.equals(sql)) {
            return where;
        }
        try {
            Expression expression = CCJSqlParserUtil.parseExpression(sql);
            // 数据权限使用单独的括号 防止与其他条件冲突
            Parenthesis parenthesis = new Parenthesis(expression);
            if (ObjectUtil.isNotNull(where)) {
                return new AndExpression(where, parenthesis);
            } else {
                return parenthesis;
            }
        } catch (Exception e) {
            throw new RuntimeException("数据权限解析异常 => " + e.getMessage());
        }
    }

    /**
     * 构建查询条件
     *
     * @param dataPermission 数据权限枚举
     * @see com.baymax.datascope.enums.DataScopeEnum
     */
    private String buildDataFilter(DataPermission dataPermission, boolean isSelect) {
        StringBuilder sqlBuilder = new StringBuilder();
        // 当前用户数据权限枚举
        DataScopeEnum dataScopeEnum = SecurityUtil.getDataScopeEnum();
        if (DataScopeEnum.ALL == dataScopeEnum) {
            return StrUtil.EMPTY;
        } else if (DataScopeEnum.CUSTOM == dataScopeEnum) {
            //TODO 自定义数据权限
            return StrUtil.EMPTY;
        } else if(DataScopeEnum.SELF == dataScopeEnum) {
            if (ObjectUtil.isNull(dataPermission)) {
                sqlBuilder.append(" create_by").append(" = ").append(SecurityUtil.getUserId());
            } else {
                sqlBuilder.append(dataPermission.createBy()).append(" = ").append(SecurityUtil.getUserId());
            }
        }
        return sqlBuilder.toString();
    }

    /**
     * 获取自定义注解
     * 通过反射的方式拿到方法上的注解。例如联查SQL，需要指定（别名.数据权限字段）拼接到where后面
     *
     * @param mappedStatementId mapper对应的查询方法
     */
    private DataPermission findDataScopeAnnotation(String mappedStatementId) {
        if (ObjectUtil.isNotNull(dataPermissionCacheMap.get(mappedStatementId))) {
            return dataPermissionCacheMap.get(mappedStatementId);
        }
        int index = StrUtil.lastIndexOfIgnoreCase(mappedStatementId, StrUtil.DOT);
        String className = mappedStatementId.substring(Constant.ZERO, index);
        String methodName = mappedStatementId.substring(index + Constant.ONE);
        Class<Object> clazz = ClassUtil.loadClass(className);
        // 获取类中匹配的所有方法
        List<Method> methodList = Arrays.stream(ClassUtil.getDeclaredMethods(clazz))
                .filter(method -> method.getName().equals(methodName)).collect(Collectors.toList());
        DataPermission dataPermission;
        // 获取方法注解
        for (Method method : methodList) {
            if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) {
                dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class);
                if (ObjectUtil.isNotNull(dataPermission)) {
                    dataPermissionCacheMap.put(mappedStatementId, dataPermission);
                    return dataPermission;
                }
            }
        }
        // 获取类注解
        if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) {
            dataPermission = clazz.getAnnotation(DataPermission.class);
            if (ObjectUtil.isNotNull(dataPermission)) {
                dataPermissionCacheMap.put(mappedStatementId, dataPermission);
                return dataPermission;
            }
        }
        return null;
    }

}
